from typing import Any, List

from fastapi.encoders import jsonable_encoder
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime
from decimal import Decimal
from pydantic import BaseModel
from afast_core.core_tools.convert_helper import name_convert_for_dict

from sqlalchemy.sql.schema import Table


def convert_table_to_model(db_model):
    NO_PRIMARY_KEY = False
    if not isinstance(db_model, Table):
        return db_model, NO_PRIMARY_KEY
    db_name = str(db_model.fullname)
    table_dict = {'__table__': db_model,
                  '__tablename__': db_name}
    if not db_model.primary_key:
        table_dict['__mapper_args__'] = {
            "primary_key": [i for i in db_model._columns]
        }
        NO_PRIMARY_KEY = True

    for i in db_model.c:
        col, = i.expression.base_columns
        table_dict[str(i.key)] = col

    return type(f'{db_name}DeclarativeBaseClass', (declarative_base(),), table_dict), NO_PRIMARY_KEY


class ModesResponse(object):

    @staticmethod
    def model_to_dict(obj, *ignore: str) -> dict:
        if getattr(obj, '__table__', None) is None:
            return obj
        data = dict()
        for c in obj.__table__.columns:
            if c.name in ignore:
                # 如果字段忽略, 则不进行转换
                continue
            val = getattr(obj, c.name)
            if isinstance(val, datetime):
                data[c.name] = val.strftime("%Y-%m-%d %H:%M:%S")
            else:
                data[c.name] = val
        return data

    @staticmethod
    def model_to_dict_name_convert_camel_case(obj, *ignore: str):
        if isinstance(obj, BaseModel):
            return name_convert_for_dict(obj.dict())
        else:
            return name_convert_for_dict(ModesResponse.model_to_dict(obj, *ignore))

    @staticmethod
    def dict_model_to_dict(obj):
        for k, v in obj.items():
            if isinstance(v, dict):
                ModesResponse.dict_model_to_dict(v)
            elif isinstance(v, list):
                obj[k] = ModesResponse.model_to_list(v)
            else:
                obj[k] = ModesResponse.model_to_dict(v)
        return obj

    @staticmethod
    def json_serialize(obj):
        ans = dict()
        for k, o in dict(obj).items():
            if isinstance(o, set):
                ans[k] = list(o)
            elif isinstance(o, datetime):
                ans[k] = o.strftime("%Y-%m-%d %H:%M:%S")
            elif isinstance(o, Decimal):
                ans[k] = str(o)
            elif isinstance(o, bytes):
                ans[k] = o.decode(encoding='utf-8')
            else:
                ans[k] = o
        return ans

    @staticmethod
    def parse_sql_result(data: List):
        columns = []
        if len(data) > 0:
            columns = list(data[0].keys())
        return columns, [ModesResponse.json_serialize(obj) for obj in data]

    @staticmethod
    def model_to_list(data: List, *ignore: str):
        return [ModesResponse.model_to_dict(x, *ignore) for x in data]

    @staticmethod
    def models_to_dicts_name_convert_camel_case(data: List, *ignore: str):
        return [ModesResponse.model_to_dict_name_convert_camel_case(x, *ignore) for x in data]
